/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#ifndef __CDIALECTINTERP_H__
#define __CDIALECTINTERP_H__

#include "DConfigGlobals.h"
#include "Interpreter.h"
#include "CDebug.h"
#include "CByteCode.h"
#include "CImplementer.h"

#define __VERSION_NUMBER__	1.14f
#define __VERSION_STRING__	_T("1.14")

#define EXTERNAL_SYMBOL	(VALUE*)0xFFFFFFFF

//////////////////////////////////////////////////////

#define MAX_CALL_DEPTH	64
struct DataStack_t {
	struct CodeStreamNode {
		int m_SavedPosition;
		CByteCode* m_CodeStream;
	};

	DataStack_t();
	int m_Top;
	CFrameImpl* m_ModuleSymTab[MAX_CALL_DEPTH];
	CFrameImpl* m_FunctionSymTab[MAX_CALL_DEPTH];
	CClassImpl* m_ClassImpl[MAX_CALL_DEPTH];
	CodeStreamNode m_CodeStreamNode[MAX_CALL_DEPTH];
	VALUE* m_pvWith[MAX_CALL_DEPTH];

	CFrameImpl* ModuleSymTab();
	CFrameImpl* FunctionSymTab();
	CClassImpl* ClassImpl();
	CByteCode* CodeStream();
	VALUE* WithValuePtr();
	void SetWithValuePtr(VALUE* pvWith);

	void Push(CFrameImpl* modSymTab, CFrameImpl* funcSymTab, CByteCode* codeStream, CClassImpl* classImpl = NULL);
	int Level() const;
	void PopToLevel(int level);
	void Pop();
	VALUE CallStack();
};


#define MAX_PATH_ENTRIES	16	// Max number of directories to store in search path


class CDialectInterp : public CInterpreter {
public:
	CDialectInterp();
	virtual ~CDialectInterp();
	bool InterpretFile(LPCTSTR filename);
	void SetEnvironVar(LPCTSTR key, VALUE value);

public:
	bool InterpretText(LPCTSTR text, VALUE context, LPCTSTR moduleName);
	bool Evaluate(LPCTSTR str, VALUE& result, VALUE context);
	bool Execute(VALUE func, VALUE context, int argCount, VALUE args[], VALUE& result);
	bool ExecFuncFromCore(VALUE& result, VALUE vFunc, int argCount, VALUE args[], VALUE* self);

	bool EvaluateFile(LPCTSTR filename, VALUE& result, VALUE& context);
	bool CompileFile(TString fileIn, TString fileOut, TString& exception);

	void ClearException();
	void SetException(TString exc, TString msg, TString module, int line, VALUE vOther);
	void SyntaxError(LPCTSTR parmString, ...);
	void RuntimeError(LPCTSTR parmString, ...);
	void InternalError(LPCTSTR parmString, ...);

	// Debugging helpers...
	VALUE CallStack();
	bool InBreakpoint();
	void Abort();	// Tell interp to quit executing and unwind stack...
	void Reset();	// Doesn't call abort, simply reinitializes symbol tables, import tables, etc.
	void SetDebugOutFunc(void (*debugOutFunc)(TString text));
	void SetBreakPointFunc(void (*breakPointFunc)(TString module, int line, CInterpreter* pInterp));
	void SetBackgroundHandler(int frequency, void (*backgroundFunc)(CInterpreter* pInterp));
	bool DebugInterpretText(LPCTSTR text, TString& exceptionText);
	bool DebugEvaluate(LPCTSTR text, VALUE& result, TString& exceptionText);
	void SetExceptionInstance(VALUE vClassInstance);

	long IncOpenWindows();
	long DecOpenWindows();
	long GetOpenWindows() const;
	int GetStackLevel() const;

	int CurrentLine();
	TString CurrentModuleName();
	TString CurrentModulePath();	// If known, path of module file
	VALUE CurrentModuleFrame();		// Symbol table for module

	void RegisterCoreModule(TString moduleName, VALUE (*initFunc)());

	TString (*m_pDefaultReadFunc)(int count);
	TString (*m_pDefaultReadLineFunc)();
	void SetDefaultRead(TString (*readFunc)(int count));
	void SetDefaultReadLine(TString (*readLineFunc)());
	void SetDefaultInputOther(TString name, VALUE vOther);
	void (*m_pDefaultWriteFunc)(TString text);
	void (*m_pDefaultWriteLineFunc)(TString text);
	void SetDefaultWrite(void (*writeFunc)(TString text));
	void SetDefaultWriteLine(void (*writeLineFunc)(TString text));
	void SetDefaultOutputOther(TString name, VALUE vOther);

protected:
	VALUE m_vDefaultInInst;
	VALUE m_vDefaultOutInst;

public:
	void SetInitialPath(TString path);
protected:
	void SetScriptPath(TString path);
	int m_PathCount;
	TString m_ScriptPath[MAX_PATH_ENTRIES];
	TString m_InitialPath;

	long m_OpenWindows;

	bool m_bInBreakpoint;
	void (*m_DebugOutFunc)(TString text);
	void (*m_BreakPointFunc)(TString module, int line, CInterpreter* pInterp);
	void (*m_BackgroundHandler)(CInterpreter* pInterp);
	int m_BackgroundHandlerFreq;
	int m_BackgroundHandlerFreqCount;

	void SetException(TString exc, TString msg, VALUE vOther);
	void EvaluateText(LPCTSTR str, VALUE& result, CFrameImpl* context);
	virtual void Error(TString title, TString message);

	virtual VALUE RequestModule(TString moduleName);
	bool LoadScript(CByteCode* cs, TString& moduleName);

public:
	bool MakeExe(TString outputFile, TString interpFilename, TString directory, TString initialScript);
	bool LoadAppendedFrame();
	bool ExecuteAppended();
protected:
	bool MakeExe_(TString outputFile, TString interpFilename, TString initialScript, VALUE blobFrame);
	bool GetAppendedScript(CByteCode* cs, TString name);
	TString m_InitialScript;
	VALUE m_AppendedFrame;	// Stores appended code blobs

protected:
	static VALUE WriteOutputDev(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp);
	VALUE m_vOutputDev;
	CFunctionImpl* m_WriteFunc;
	CFunctionImpl* m_TabFunc;
	CFunctionImpl* m_CRFunc;
	CFunctionImpl* m_TableFunc;
	CFunctionImpl* m_LinesLeftFunc;
	CFunctionImpl* m_SectItemFunc;
	CFunctionImpl* m_StartSectFunc;
	CFunctionImpl* m_EndSectFunc;

	void TextOut(LPCTSTR str);
	void TextOutFormated(LPCTSTR parmString, ...);

	DataStack_t m_DataStack;	// Call stack info

	bool Interpret(TokenTypes breakOnTok1 = tokEOF, TokenTypes breakOnTok2 = tokEOF,
		TokenTypes breakOnTok3 = tokEOF, TokenTypes breakOnTok4 = tokEOF, TokenTypes breakOnTok5 = tokEOF);

	long SkipBlock(TokenTypes headTok, TokenTypes tailTok,
		TokenTypes otherTok1 = tokUndefined, TokenTypes otherTok2 = tokUndefined);

	VALUE* FindValuePtr(TString name, bool bCreateOK);
	virtual bool IsExternal(TString symbolName);
	virtual void SetExternal(TString symbolName, VALUE value);
	virtual VALUE GetExternal(TString symbolName);
	virtual VALUE CallExternal(TString symbolName, int argCount, VALUE args[]);
	VALUE RHSExternal(TString symbolName);	// Used in expression parser

	bool Require(TokenTypes tok, LPCTSTR message);

	bool FindNextCatch();	// See DoTry()

	void DoThrow();
	void DoTry();
	void DoImport();
	void DoDebugOut();
	void DoPrint();
	void DoPersistComment();
	void DoReturn();
	void DoAssignment();
	void DoIfThenElse();
	void DoForLoop();
	void DoForEach();
	void DoWhileLoop();
	void DoFuncDef();
	void ParseFunctionBody(VALUE& func);
	void DoArrayAssignment();
	void DoRun();
	void DoClass();
	void DoSItem();
	void DoStartSect();
	void DoEndSect();
	void DoWith();
	void DoLocal();
	void DoInterpret();

	void Unserialize(TString filename, VALUE& result);

public:
	int GetReturnFlag() const;	// returns the current value of m_bReturning below
	VALUE GetException();		// returns the current value of m_vException below
	int m_bReturning;
	VALUE m_vException;		// If m_bReturning == true and m_vException.Type() == stFrame
							// Then we are passing a thrown exception up the call stack

protected:
	static VALUE CantWriteMe(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp);
	static VALUE WriteScriptPath(VALUE* self, int argCount, VALUE args[], CInterpreter* pInterp);

	TString Field(TString from, TString split, int fieldNum);
	bool funcField_(VALUE* result);

	friend CClassInstImpl;	// Used by destructor
	bool ExecFunc(VALUE& result, CFunctionImpl* func, int _argCount = -1, VALUE _args[] = NULL, VALUE* vThis = NULL);
	bool ExecDLLFunc(VALUE& result, void* func, int argCount = -1, VALUE args[] = NULL);

	bool IntExpression(int& result);		// Return false if exception occured
	bool FloatExpression(float& result);	// Return false if exception occured

	bool Expression(VALUE& result);			// Return false if exception occured
	bool _Expression(VALUE *result);
	inline bool Question(VALUE *result);				// ?:
	inline bool FirstRelational(VALUE *result);		// ==, !=
	inline bool SecondRelational(VALUE *result);		// >, >=, <, <=
	inline bool FirstBinary(VALUE *result);			// +, -, or
	inline bool SecondBinary(VALUE *result);			// *, /, and
	inline bool Unary(VALUE *result);					// unary -, (not and bit-not)
	inline bool DotExpr(VALUE *result);				// ., [], ()
	bool ReadNumber(VALUE *result, VALUE** pvThis);		//
	VALUE* LHSExpression(TString* symbolName, int& chunkIndex);
	void SkipExpression(TokenTypes stopToken);	// Used to short-circuit certain operations
	int Precedence(TokenTypes token); 			// Used mainly by SkipExpression()
	bool Concat(VALUE a, VALUE b, VALUE* result);

	VALUE ParseArgsList();
	VALUE ParseListDeclaration();	// Can contain anything - eg, foovar[1, "hello", [3:5]]
	VALUE ParseDictDeclaration();
	bool ParseChunkDecl(VALUE& vResult);

	VALUE m_RegCoreModules;	// Store init functions pointers registered with RegisterCoreModule()
	VALUE m_SpecialSymTab;
	VALUE m_GlobalSymTab;
	VALUE m_SymbolTables;	// Contains 'global', globally imported modules, forms, etc.
	TString ModuleNameFromFilename(TString filename);
};

#endif	// __CDIALECTINTERP_H__
